import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

public class MemoryManager {
    private static final Logger LOGGER = Logger.getLogger(MemoryManager.class.getName());

    private final int memorySize;
    private final List<MemoryBlock> freeBlocks = new ArrayList<>();
    private final Map<Integer, MemoryBlock> allocatedBlocks = new HashMap<>();

    public MemoryManager(int memorySize) {
        this.memorySize = memorySize;
        freeBlocks.add(new MemoryBlock(0, memorySize));
    }

    /**
     * 根据最坏适应算法分配内存给指定进程。
     */
    public void allocate(int processId, int size) throws IllegalArgumentException, IllegalStateException {
        if (size <= 0) {
            throw new IllegalArgumentException("分配大小必须为正数。");
        }

        Optional<MemoryBlock> worstFitBlock = freeBlocks.stream()
                .filter(block -> block.getSize() >= size)
                .max(Comparator.comparingInt(MemoryBlock::getSize));

        if (worstFitBlock.isPresent()) {
            MemoryBlock block = worstFitBlock.get();
            MemoryBlock allocatedBlock = new MemoryBlock(block.getStart(), size);
            allocatedBlocks.put(processId, allocatedBlock);

            updateFreeBlocks(block, size);

            logAction("已分配", processId, allocatedBlock);
        } else {
            throw new IllegalStateException(String.format("没有足够的内存分配给进程 %d。", processId));
        }
    }

    /**
     * 批量分配内存给多个进程。
     */
    public void batchAllocate(Map<Integer, Integer> allocations) {
        for (Map.Entry<Integer, Integer> entry : allocations.entrySet()) {
            try {
                allocate(entry.getKey(), entry.getValue());
            } catch (IllegalArgumentException | IllegalStateException e) {
                System.out.println(e.getMessage());
            }
        }
    }

    /**
     * 回收指定进程的内存。
     */
    public void deallocate(int processId) {
        if (allocatedBlocks.containsKey(processId)) {
            MemoryBlock block = allocatedBlocks.remove(processId);
            freeBlocks.add(block);
            mergeAdjacentFreeBlocks();

            logAction("已回收", processId, block);
        } else {
            System.out.printf("进程 %d 没有分配或已经被回收。\n", processId);
        }
    }

    /**
     * 更新空闲分区列表，根据已分配内存调整剩余空闲内存。
     */
    private void updateFreeBlocks(MemoryBlock block, int size) {
        int remainingSize = block.getSize() - size;
        if (remainingSize > 0) {
            freeBlocks.set(freeBlocks.indexOf(block),
                    new MemoryBlock(block.getStart() + size, remainingSize));
        } else {
            freeBlocks.remove(block);
        }
    }

    /**
     * 合并相邻的空闲内存块。
     */
    private void mergeAdjacentFreeBlocks() {
        Collections.sort(freeBlocks, Comparator.comparingInt(MemoryBlock::getStart));
        List<MemoryBlock> mergedBlocks = new ArrayList<>(freeBlocks);
        for (int i = 1; i < mergedBlocks.size(); ) {
            MemoryBlock prev = mergedBlocks.get(i - 1);
            MemoryBlock current = mergedBlocks.get(i);
            if (prev.getEnd() == current.getStart()) {
                mergedBlocks.set(i - 1, new MemoryBlock(prev.getStart(), prev.getSize() + current.getSize()));
                mergedBlocks.remove(i);
            } else {
                i++;
            }
        }
        freeBlocks.clear();
        freeBlocks.addAll(mergedBlocks);
    }

    /**
     * 记录内存分配或回收的动作。
     */
    private void logAction(String action, int processId, MemoryBlock block) {
        LOGGER.log(Level.INFO, "{0} 内存给进程 {1} 从 {2} 到 {3}",
                new Object[]{action, processId, block.getStart(), block.getEnd()});
        System.out.printf("%s 内存给进程 %d 从 %dKB 到 %dKB\n",
                action, processId, block.getStart(), block.getEnd());
    }

    /**
     * 展示当前内存状态，包括已分配和空闲的内存块。
     */
    public void showStatus() {
        System.out.println("当前内存状态：");
        System.out.println("已分配的块：");
        allocatedBlocks.forEach((processId, block) ->
                System.out.printf("进程 %d: 起始=%dKB, 大小=%dKB\n", processId, block.getStart(), block.getSize())
        );

        System.out.println("\n空闲的块：");
        freeBlocks.forEach(block ->
                System.out.printf("起始=%dKB, 大小=%dKB\n", block.getStart(), block.getSize())
        );
    }

    /**
     * 内存块类，表示一个内存块的信息。
     */
    static class MemoryBlock {
        private final int start;
        private final int size;

        public MemoryBlock(int start, int size) {
            this.start = start;
            this.size = size;
        }

        public int getStart() {
            return start;
        }

        public int getSize() {
            return size;
        }

        public int getEnd() {
            return start + size;
        }

        @Override
        public String toString() {
            return "MemoryBlock{" +
                    "start=" + start +
                    ", size=" + size +
                    '}';
        }
    }

    /**
     * 提供批量分配内存的交互界面。
     */
    private static void batchAllocateMemory(Scanner scanner, MemoryManager manager) {
        try {
            System.out.print("请输入要分配的进程数量：");
            int numProcesses = Integer.parseInt(scanner.nextLine().trim());
            Map<Integer, Integer> allocations = new HashMap<>();
            for (int i = 0; i < numProcesses; i++) {
                System.out.printf("进程 %d 的信息：\n", i + 1);
                System.out.print("请输入进程ID：");
                int processId = Integer.parseInt(scanner.nextLine().trim());
                System.out.print("请输入要分配的内存大小（KB）：");
                int size = Integer.parseInt(scanner.nextLine().trim());
                allocations.put(processId, size);
            }
            manager.batchAllocate(allocations);
        } catch (NumberFormatException e) {
            System.out.println("无效的数字格式。请检查输入并重试。");
        }
    }

    /**
     * 提供回收内存的交互界面。
     */
    private static void deallocateMemory(Scanner scanner, MemoryManager manager) {
        try {
            System.out.print("请输入要回收的进程ID：");
            int processId = Integer.parseInt(scanner.nextLine().trim());
            manager.deallocate(processId);
        } catch (NumberFormatException e) {
            System.out.println("无效的数字格式。请检查输入并重试。");
        }
    }

    /**
     * 主方法，提供命令行界面供用户交互。
     */
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        MemoryManager manager = null;
        try {
            System.out.print("请输入总内存大小（KB）：");
            int memorySize = Integer.parseInt(scanner.nextLine().trim());
            manager = new MemoryManager(memorySize);

            while (true) {
                System.out.println("\n请选择操作：");
                System.out.println("1. 批量分配内存");
                System.out.println("2. 回收内存");
                System.out.println("3. 显示状态");
                System.out.println("4. 退出程序");

                System.out.print("您的选择：");
                String choice = scanner.nextLine().trim();
                switch (choice) {
                    case "1":
                        batchAllocateMemory(scanner, manager);
                        break;
                    case "2":
                        deallocateMemory(scanner, manager);
                        break;
                    case "3":
                        manager.showStatus();
                        break;
                    case "4":
                        System.out.println("感谢您使用内存管理器。再见！");
                        return;
                    default:
                        System.out.println("无效选项，请重新选择。");
                }
            }
        } catch (Exception e) {
            System.err.println("发生错误：" + e.getMessage());
            LOGGER.log(Level.SEVERE, "An error occurred", e);
        } finally {
            if (manager != null) {
                System.out.println("程序总结：");
                System.out.println("总内存大小：" + manager.memorySize + "KB");
                System.out.println("已分配的块数量：" + manager.allocatedBlocks.size());
                System.out.println("空闲的块数量：" + manager.freeBlocks.size());
                manager.showStatus();
            }
            System.out.println("再见！");
            scanner.close();
        }
    }
}